package com.lyd.productanalyse.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.lyd.productanalyse.entity.ExcelCalResultDomain;
import com.lyd.productanalyse.entity.ProductDomain;
import com.lyd.productanalyse.service.ExcelReaderService;
import com.lyd.productanalyse.utils.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.File;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;

/**
 * 描述 excel 读取服务实现
 *
 * @author liyadong
 * @created 2023-11-20 10:19
 */
@Service
public class ExcelReaderServiceImpl implements ExcelReaderService {
    /**
     * 日志打印
     */
    private final static Logger logger = LoggerFactory.getLogger(ExcelReaderServiceImpl.class);
    /**
     * unknown
     */
    private final static String UNKNOWN = "UNKNOWN";


    /**
     * 读取Excel文件并输出内容
     *
     * @param fileName 文件名
     * @param sheetIndex 工作表索引
     * @param groupField 分组字段
     */
    @Override
    public ExcelCalResultDomain readExcel(String fileName, int sheetIndex, String groupField) {
        logger.info("解析文件名：{}, sheet索引号：{}, 分组索引号：{}", fileName, sheetIndex, groupField);
        // 将文件名转换为File对象
        File excelFile = FileUtil.file(fileName);
        // 使用EasyExcel读取Excel文件，并指定工作表索引
        logger.info("开始读取表格数据");
        List<ProductDomain> productDomains = EasyExcel.read(excelFile).head(ProductDomain.class).sheet(sheetIndex).doReadSync();
        // 总计
        logger.info("开始计算总计");
        BigDecimal total = productDomains.stream().map(ProductDomain::getCalResultValue).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 将数据根据指定字段分组
        Map<String, List<ProductDomain>> productDomainMap = new HashMap<>(1000);
        logger.info("开始根据指定列进行分组");
        productDomains.forEach(productDomain -> {
            String groupKey = getGroupKey(productDomain, groupField);
            if (productDomainMap.containsKey(groupKey)) {
                productDomainMap.get(groupKey).add(productDomain);
            } else {
                List<ProductDomain> productDomainList = new ArrayList<>();
                productDomainList.add(productDomain);
                productDomainMap.put(groupKey, productDomainList);
            }
        });
        // 分组后根据 |Quantity * Material Costs|进行计算
        Map<String, BigDecimal> productResultMap = new HashMap<>(productDomainMap.keySet().size());
        logger.info("开始计算分组后价格");
        productDomainMap.forEach((key, value) -> productResultMap.put(key, value.stream().map(ProductDomain::getCalResultValue).reduce(BigDecimal.ZERO, BigDecimal::add)));
        // 排序
        logger.info("开始按照价格排序");
        Map<String, BigDecimal> sortProductResultMap = MapUtils.sortMapByValues(productResultMap);
        ExcelCalResultDomain excelCalResultDomain = new ExcelCalResultDomain();
        excelCalResultDomain.setTotal(total);
        excelCalResultDomain.setSortProductResultMap(sortProductResultMap);
        return excelCalResultDomain;
    }

    /**
     * 根据分组索引获取分组字段
     * @param productDomain 商品实体类
     * @param groupField 分组字段（如：8,10表示按照索引号为8和10的字段进行分组）
     * @return 分组字段
     */
    private String getGroupKey(ProductDomain productDomain, String groupField) {
        StringBuilder groupKey = new StringBuilder();
        List<String> groupFieldList = Arrays.asList(groupField.split(StrPool.COMMA));
        Field[] fields = ReflectUtil.getFields(ProductDomain.class);
        for (Field field : fields) {
            if (field.isAnnotationPresent(ExcelProperty.class)) {
                ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
                if (groupFieldList.contains(annotation.index()+"")) {
                    Object fieldValue = ReflectUtil.getFieldValue(productDomain, field.getName());
                    groupKey.append(fieldValue == null ? UNKNOWN : fieldValue).append(StrPool.AT);
                }
            }
        }
        return groupKey.deleteCharAt(groupKey.length() - 1).toString();
    }

}
